﻿using System;
using System.Security;
using System.ServiceModel;
using System.ServiceModel.Channels;
using Sebarf.Communication;
using Sebarf.Core;
using Sebarf.Core.Interfaces;

namespace Sebarf.Client {
	/// <summary>
	/// Client handler class managing the connection 
	/// via WCF to the InterLINQ Server.
	/// </summary>
	/// <seealso cref="ClientQueryHandler"/>
	/// <seealso cref="InterLinqQueryHandler"/>
	/// <seealso cref="IQueryHandler"/>
	public class WcfBasedClient : IClient {
		#region Delegates

		public delegate IRequestHandlerEngine _RequestHandlerEngineIfcFactory(string callerId);

		#endregion

		#region Public Properties

		public Binding Binding { get; private set; }
		public EndpointAddress EndpointAddress { get; private set; }

		public IRemoteRequestHandlerEngine Channel { get; private set; }

		public string CallerId { get; set; }

		public IRequestHandlerEngine RequestHandlerEngine { get; private set; }

		#endregion

		private string ServiceURI;

		#region Methods

		/// <summary>
		/// Connects to the server.
		/// <see cref="InterLinqQueryHandler"/>
		/// </summary>
		/// <seealso cref="ClientQueryHandler.Connect"/>
		public void Connect() {
			if (Binding == null || EndpointAddress == null) {
				Connect(ServiceHelper.GetNetTcpBinding(), ServiceHelper.GetEndpoint(ServiceURI));
			}
			else {
				Connect(Binding, EndpointAddress);
			}
		}

		public static IClient Create(string callerId, IRequestHandlerEngine engine) {
			return Create(callerId, engine, ServiceHelper.GetServiceUri());
		}

		public static IClient Create(string callerId, IRequestHandlerEngine engine, string serviceURI) {
			return new WcfBasedClient { CallerId = callerId, RequestHandlerEngine = engine, ServiceURI = serviceURI };
		}

		/// <summary>
		/// Connects to the server.
		/// </summary>
		/// <param name="binding">Predefined <see cref="Binding"/>.</param>
		/// <param name="endpointAddress"><see cref="EndpointAddress"/> of the server.</param>
		public void Connect(Binding binding, EndpointAddress endpointAddress) {
			Binding = binding;
			EndpointAddress = endpointAddress;
			var channelFactory = new ChannelFactory<IRemoteRequestHandlerEngine>(binding, endpointAddress);
			Channel = channelFactory.CreateChannel();
		}

		#endregion

		#region IClient Members

		/// <summary>
		/// Processes the given request on the server.
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		public object Process(IRequest request) {
			object toReturn = Channel.Process(CallerId, request);
			if (toReturn is UnableToProcessRequestReturnValue) {
				var faultReason = toReturn as UnableToProcessRequestReturnValue;
				switch (faultReason.UnableToProcessRequestReason) {
					case UnableToProcessRequestReturnValue.Reason.Security:
						throw new SecurityException();
						break;
					case UnableToProcessRequestReturnValue.Reason.NotSupported:
						throw new NotSupportedException();
						break;
					case UnableToProcessRequestReturnValue.Reason.Unknown:
						throw new Exception();
						break;
					default:
						throw new Exception();
						break;
				}
			}
			return toReturn;
		}

		/// <summary>
		/// Processes the given request locally
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		public object ProcessLocal(IRequest request) {
			return RequestHandlerEngine.Process(request);
		}

		#endregion
	}
}